//
//  CMSocketController.m
//  Perilla
//
//  Created by Martin Schiøth Dyrby on 10/6/12.
//  Copyright (c) 2012 Martin Schiøth Dyrby. All rights reserved.
//

#import "CMSocketController.h"
#import "DDLog.h"
#import "DDTTYLogger.h"

#define FORMAT(format, ...) [NSString stringWithFormat:(format), ##__VA_ARGS__]

#define READ_TIMEOUT 15.0

// Log levels: off, error, warn, info, verbose
static const int ddLogLevel = LOG_LEVEL_INFO;

@interface CMSocketController (PrivateAPI)

- (void)logError:(NSString *)msg;
- (void)logInfo:(NSString *)msg;
- (void)logMessage:(NSString *)msg;

@end

@implementation CMSocketController

@synthesize numSockets;
@synthesize objectController;
@synthesize logView;

- (id)init
{
	if((self = [super init]))
	{
        [[NSNotificationCenter defaultCenter]
         addObserver:self
         selector: @selector(midiMessageMatch:)
         name:@"midiMessageMatch"
         object: nil];
        
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
        asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue];
        
		connectedSockets = [[NSMutableArray alloc] initWithCapacity:1];
        
		isRunning = NO;
        
        uint16_t port = 65000;
        
		NSError *error = nil;
		if(![asyncSocket acceptOnPort:port error:&error])
		{
			[self logError:FORMAT(@"Error starting server: %@", error)];
			return self;
		}
		
		[self logInfo:FORMAT(@"Echo server started on port %hu", [asyncSocket localPort])];
		isRunning = YES;
    }
    return self;
}

-(void) midiMessageMatch: (NSNotification *) note {
    NSDictionary* dict = (NSDictionary*)note.object;
    NSString* name = [dict valueForKey:@"name"];
    float value = [[dict valueForKey:@"value"] floatValue];
    
    NSString* dataStr =[NSString stringWithFormat:@"midi;%@;%f", name, value];
	NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
    for(int i = 0; i < [connectedSockets count]; i++) {
        GCDAsyncSocket* socket = [connectedSockets objectAtIndex:i];
        [socket writeData:data withTimeout:-1 tag:0];
    }
}

- (void)logError:(NSString *)msg
{
    if(logView == nil) return;
    NSString* cur = (logView.string == nil) ? @"" : logView.string;
    [self.logView setString:[msg stringByAppendingString: cur]];
    [self.logView setString:[@"\n" stringByAppendingString: logView.string]];
    NSLog(@"log: %@", msg);
}

- (void)logInfo:(NSString *)msg
{
    [self logError:msg];
}

- (void)logMessage:(NSString *)msg
{
    [self logError:msg];
}


- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
	@synchronized(connectedSockets)
	{
		[connectedSockets addObject:newSocket];
        [self setValue:[NSNumber numberWithUnsignedLong:[connectedSockets count]] forKey:@"numSockets"];
	}
	
	NSString *host = [newSocket connectedHost];
	UInt16 port = [newSocket connectedPort];
	
	dispatch_async(dispatch_get_main_queue(), ^{
		@autoreleasepool {
            
			[self logInfo:FORMAT(@"Accepted client %@:%hu", host, port)];
            [self logInfo:FORMAT(@"Num sockets %lu", (unsigned long)numSockets)];
            
		}
	});
	
	NSString *welcomeMsg = @"Connected to Perilla Server.\r\n";
	NSData *welcomeData = [welcomeMsg dataUsingEncoding:NSUTF8StringEncoding];
	
	[newSocket writeData:welcomeData withTimeout:-1 tag:0];
	
	[newSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:0];
}

- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag
                 elapsed:(NSTimeInterval)elapsed
               bytesDone:(NSUInteger)length
{
	return READ_TIMEOUT;
}


- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
	// This method is executed on the socketQueue (not the main thread)
}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
	// This method is executed on the socketQueue (not the main thread)
	
	dispatch_async(dispatch_get_main_queue(), ^{
		@autoreleasepool {
            NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys: data, @"data", nil];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"socketMessageReceived" object:dict];
            

            /*
			NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length] - 2)];
            NSString* name = @"TMP_NAME";
            NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:strData, @"data", name, @"name", nil];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"addNewObject" object:dict];
			NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
			if (msg)
			{
				[self logMessage:msg];
			}
			else
			{
				[self logError:@"Error converting received data into UTF-8 String"];
			}
            */
		}
	});
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
	if (sock != asyncSocket)
	{
		dispatch_async(dispatch_get_main_queue(), ^{
			@autoreleasepool {
                
				[self logInfo:FORMAT(@"Client Disconnected")];
                
			}
		});
		
		@synchronized(connectedSockets)
		{
			[connectedSockets removeObject:sock];
            [self setValue:[NSNumber numberWithUnsignedLong:[connectedSockets count]] forKey:@"numSockets"];
		}
	}
}

@end
